import 'dart:convert';
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_webrtc/flutter_webrtc.dart';

import '../utils/screen_select_dialog.dart';
import 'random_string.dart';

import '../utils/device_info.dart'
if (dart.library.js) '../utils/device_info_web.dart';
import '../utils/websocket.dart'
if (dart.library.js) '../utils/websocket_web.dart';
import '../utils/turn.dart' if (dart.library.js) '../utils/turn_web.dart';

// 定义信令状态枚举，用于表示与信令服务器的连接状态
enum SignalingState {
  ConnectionOpen,  // 连接打开
  ConnectionClosed, // 连接关闭
  ConnectionError, // 连接错误
}

// 定义通话状态枚举，用于表示通话的不同阶段
enum CallState {
  CallStateNew,      // 新通话状态
  CallStateRinging,  // 通话振铃状态
  CallStateInvite,   // 通话邀请状态
  CallStateConnected,// 通话连接状态
  CallStateBye,      // 通话结束状态
}

// 定义视频源枚举，用于表示视频的来源
enum VideoSource {
  Camera,  // 摄像头视频源
  Screen,  // 屏幕共享视频源
}

// 定义会话类，用于管理单个会话的信息
class Session {
  // 构造函数，接受会话ID（sid）和对方ID（pid）
  Session({required this.sid, required this.pid});
  String pid;  // 对方ID
  String sid;  // 会话ID
  RTCPeerConnection? pc;  // RTCPeerConnection实例，用于处理音视频通信
  RTCDataChannel? dc;  // RTCDataChannel实例，用于处理数据通道
  List<RTCIceCandidate> remoteCandidates = [];  // 远程ICE候选者列表
}

// 定义信令类，处理与信令服务器的连接和信令相关操作
class Signaling {
  // 构造函数，接受信令服务器地址（_host）和BuildContext（_context）
  Signaling(this._host, this._context);

  JsonEncoder _encoder = JsonEncoder();  // JSON编码器，用于将数据编码为JSON格式
  JsonDecoder _decoder = JsonDecoder();  // JSON解码器，用于将JSON数据解码

  String _selfId = randomNumeric(6);  // 本地用户ID，通过随机数字生成，长度为6
  SimpleWebSocket? _socket;  // 简单WebSocket实例，用于与服务器进行通信
  BuildContext? _context;  // BuildContext，用于构建UI和处理上下文相关操作
  var _host;  // 信令服务器地址
  var _port = 8086;  // 信令服务器端口

  var _turnCredential;  // TURN服务器的凭证，用于获取TURN服务

  Map<String, Session> _sessions = {};  // 存储所有会话的Map，键为会话ID，值为Session实例

  MediaStream? _localStream;  // 本地媒体流，包含本地的音频和视频轨道
  List<MediaStream> _remoteStreams = <MediaStream>[];  // 远程媒体流列表

  List<RTCRtpSender> _senders = <RTCRtpSender>[];  // RTCRtpSender列表，用于发送媒体流

  VideoSource _videoSource = VideoSource.Camera;  // 当前视频源，默认为摄像头

  // 信令状态变化的回调函数，当信令状态改变时调用
  Function(SignalingState state)? onSignalingStateChange;
  // 通话状态变化的回调函数，当通话状态改变时调用
  Function(Session session, CallState state)? onCallStateChange;
  // 本地媒体流回调函数，当本地媒体流可用时调用
  Function(MediaStream stream)? onLocalStream;
  // 远程媒体流添加回调函数，当远程媒体流添加时调用
  Function(Session session, MediaStream stream)? onAddRemoteStream;
  // 远程媒体流移除回调函数，当远程媒体流移除时调用
  Function(Session session, MediaStream stream)? onRemoveRemoteStream;
  // 在线用户更新回调函数，当在线用户列表更新时调用
  Function(dynamic event)? onPeersUpdate;
  // 数据通道消息回调函数，当数据通道有消息时调用
  Function(Session session, RTCDataChannel dc, RTCDataChannelMessage data)?
  onDataChannelMessage;
  // 数据通道创建回调函数，当数据通道创建时调用
  Function(Session session, RTCDataChannel dc)? onDataChannel;

  // 获取SDP语义，这里使用统一计划（unified-plan）
  String get sdpSemantics => 'unified-plan';

  // ICE服务器配置，包含TURN服务器等信息，用于音视频通信的网络配置
  Map<String, dynamic> _iceServers = {
    'iceServers': [
      {
        'url': 'turn:43.139.39.5:3478',  // TURN服务器地址和端口
        'username': 'root',  // TURN服务器用户名
        'credential': 'root'  // TURN服务器密码
      },
      // {
      //   'url': "turns:43.139.39.5:5349",
      //   'username': "root",
      //   'credential': "root"
      // },
      // {
      //   'url': 'stun:43.139.39.5:3478'
      // },
      // {
      //   'url': 'turn:47.99.81.108:3478',
      //   'username': 'root',
      //   'credential': 'root'
      // },
      /*
       * turn server configuration example.
      {
        'url': 'turn:123.45.67.89:3478',
        'username': 'change_to_real_user',
        'credential': 'change_to_real_secret'
      },
      */
    ]
  };

  // 媒体流配置，包含强制和可选的配置参数
  final Map<String, dynamic> _config = {
    'mandatory': {},  // 强制配置参数，这里为空
    'optional': [
      {'DtlsSrtpKeyAgreement': true},  // 可选配置参数，启用DTLS-SRTP密钥协商
    ]
  };

  // 数据通道约束，包含强制和可选的约束条件
  final Map<String, dynamic> _dcConstraints = {
    'mandatory': {
      'OfferToReceiveAudio': false,  // 不接收音频
      'OfferToReceiveVideo': false,  // 不接收视频
    },
    'optional': [],  // 没有可选约束
  };

  // 关闭信令连接并清理所有会话
  close() async {
    await _cleanSessions();  // 清理所有会话，包括关闭相关连接和释放资源
    _socket?.close();  // 关闭WebSocket连接
  }

  // 切换摄像头
  void switchCamera() {
    if (_localStream != null) {
      if (_videoSource != VideoSource.Camera) {
        // 遍历RTCRtpSender列表，替换视频轨道为本地视频流的视频轨道
        _senders.forEach((sender) {
          if (sender.track!.kind == 'video') {
            sender.replaceTrack(_localStream!.getVideoTracks()[0]);
          }
        });
        _videoSource = VideoSource.Camera;  // 设置视频源为摄像头
        onLocalStream?.call(_localStream!);  // 调用本地媒体流回调函数，通知本地媒体流已更新
      } else {
        Helper.switchCamera(_localStream!.getVideoTracks()[0]);  // 调用Helper类的switchCamera方法切换摄像头
      }
    }
  }

  // 切换到屏幕共享
  void switchToScreenSharing(MediaStream stream) {
    if (_localStream != null && _videoSource != VideoSource.Screen) {
      // 遍历RTCRtpSender列表，替换视频轨道为屏幕共享流的视频轨道
      _senders.forEach((sender) {
        if (sender.track!.kind == 'video') {
          sender.replaceTrack(stream.getVideoTracks()[0]);
        }
      });
      onLocalStream?.call(stream);  // 调用本地媒体流回调函数，通知本地媒体流已更新
      _videoSource = VideoSource.Screen;  // 设置视频源为屏幕共享
    }
  }

  // 静音麦克风
  void muteMic() {
    if (_localStream != null) {
      bool enabled = _localStream!.getAudioTracks()[0].enabled;  // 获取当前麦克风状态
      _localStream!.getAudioTracks()[0].enabled = !enabled;  // 切换麦克风启用状态
    }
  }

  // 邀请用户进行通话或数据传输
  void invite(String peerId, String media, bool useScreen) async {
    var sessionId = _selfId + '-' + peerId;  // 生成会话ID

    // 创建会话，传入当前会话（null表示新会话）、对方ID、会话ID、媒体类型和是否屏幕共享
    Session session = await _createSession(null,
        peerId: peerId,
        sessionId: sessionId,
        media: media,
        screenSharing: useScreen);
    _sessions[sessionId] = session;  // 将新创建的会话添加到会话列表中

    if (media == 'data') {
      _createDataChannel(session);  // 如果是数据传输，创建数据通道
    }
    _createOffer(session, media);  // 创建并发送邀请

    onCallStateChange?.call(session, CallState.CallStateNew);  // 调用通话状态变化回调函数，通知通话状态为新通话
    onCallStateChange?.call(session, CallState.CallStateInvite);  // 调用通话状态变化回调函数，通知通话状态为邀请
  }

  // 结束通话
  void bye(String sessionId) {
    _send('bye', {
      'session_id': sessionId,  // 会话ID
      'from': _selfId  // 本地用户ID
    });
    var sess = _sessions[sessionId];  // 获取指定会话
    if (sess != null) {
      _closeSession(sess);  // 关闭会话
    }
  }

  // 接受通话
  void accept(String sessionId, String media) {
    var session = _sessions[sessionId];  // 获取指定会话
    if (session == null) {
      return;
    }
    _createAnswer(session, media);  // 创建并发送应答
  }

  // 拒绝通话
  void reject(String sessionId) {
    var session = _sessions[sessionId];  // 获取指定会话
    if (session == null) {
      return;
    }
    bye(session.sid);  // 结束通话
  }

  // 处理接收到的消息
  void onMessage(message) async {
    Map<String, dynamic> mapData = message;  // 将接收到的消息转换为Map
    var data = mapData['data'];  // 获取消息中的数据部分

    // 根据消息类型进行不同的处理
    switch (mapData['type']) {
      case 'peers':
        {
          List<dynamic> peers = data;  // 获取在线用户列表
          if (onPeersUpdate != null) {
            Map<String, dynamic> event = Map<String, dynamic>();
            event['self'] = _selfId;  // 设置本地用户ID
            event['peers'] = peers;  // 设置在线用户列表
            onPeersUpdate?.call(event);  // 调用在线用户更新回调函数
          }
        }
        break;
      case 'offer':
        {
          var peerId = data['from'];  // 获取发送邀请的对方ID
          var description = data['description'];  // 获取邀请的描述
          var media = data['media'];  // 获取媒体类型
          var sessionId = data['session_id'];  // 获取会话ID
          var session = _sessions[sessionId];  // 获取当前会话

          // 创建新会话，传入当前会话、对方ID、会话ID、媒体类型和是否屏幕共享（这里为false）
          var newSession = await _createSession(session,
              peerId: peerId,
              sessionId: sessionId,
              media: media,
              screenSharing: false);
          _sessions[sessionId] = newSession;  // 更新会话列表

          // 设置远程描述
          await newSession.pc?.setRemoteDescription(
              RTCSessionDescription(description['sdp'], description['type']));

          // await _createAnswer(newSession, media);

          if (newSession.remoteCandidates.length > 0) {
            // 遍历远程候选者列表，将候选者添加到RTCPeerConnection中
            newSession.remoteCandidates.forEach((candidate) async {
              await newSession.pc?.addCandidate(candidate);
            });
            newSession.remoteCandidates.clear();  // 清空远程候选者列表
          }
          onCallStateChange?.call(newSession, CallState.CallStateNew);  // 调用通话状态变化回调函数，通知通话状态为新通话
          onCallStateChange?.call(newSession, CallState.CallStateRinging);  // 调用通话状态变化回调函数，通知通话状态为振铃
        }
        break;
      case 'answer':
        {
          var description = data['description'];  // 获取应答的描述
          var sessionId = data['session_id'];  // 获取会话ID
          var session = _sessions[sessionId];  // 获取当前会话

          // 设置远程描述
          session?.pc?.setRemoteDescription(
              RTCSessionDescription(description['sdp'], description['type']));
          onCallStateChange?.call(session!, CallState.CallStateConnected);  // 调用通话状态变化回调函数，通知通话状态为已连接
        }
        break;
      case 'candidate':
        {
          var peerId = data['from'];  // 获取发送候选者的对方ID
          var candidateMap = data['candidate'];  // 获取候选者信息
          var sessionId = data['session_id'];  // 获取会话ID
          var session = _sessions[sessionId];  // 获取当前会话

          // 创建RTCIceCandidate实例
          RTCIceCandidate candidate = RTCIceCandidate(candidateMap['candidate'],
              candidateMap['sdpMid'], candidateMap['sdpMLineIndex']);

          if (session != null) {
            if (session.pc != null) {
              await session.pc?.addCandidate(candidate);  // 将候选者添加到RTCPeerConnection中
            } else {
              session.remoteCandidates.add(candidate);  // 将候选者添加到远程候选者列表中
            }
          } else {
            _sessions[sessionId] = Session(pid: peerId, sid: sessionId)
              ..remoteCandidates.add(candidate);  // 创建新会话并添加候选者
          }
        }
        break;
      case 'leave':
        {
          var peerId = data as String;  // 获取离开的对方ID
          _closeSessionByPeerId(peerId);  // 根据对方ID关闭会话
        }
        break;
      case 'bye':
        {
          var sessionId = data['session_id'];  // 获取会话ID
          print('bye: ' + sessionId);  // 打印会话ID
          var session = _sessions.remove(sessionId);  // 从会话列表中移除会话

          if (session != null) {
            onCallStateChange?.call(session, CallState.CallStateBye);  // 调用通话状态变化回调函数，通知通话状态为结束
            _closeSession(session);  // 关闭会话
          }
        }
        break;
      case 'keepalive':
        {
          print('keepalive response!');  // 打印保持活动响应信息
        }
        break;
      default:
        break;
    }
  }

// 连接到信令服务器的异步方法
  Future<void> connect() async {
    // 构建WebSocket连接的URL，使用HTTPS协议和指定的主机与端口
    var url = 'https://$_host:$_port/ws';
    // 创建WebSocket实例，用于与信令服务器通信
    _socket = SimpleWebSocket(url);

    // 打印连接信息，方便调试
    print('connect to $url');

    // 如果TURN服务器凭证为空，则尝试获取凭证
    if (_turnCredential == null) {
      try {
        // 调用getTurnCredential方法从服务器获取TURN凭证
        _turnCredential = await getTurnCredential(_host, _port);
        /*
        TURN凭证示例格式:
        {
            "username": "1584195784:mbzrxpgjys",
            "password": "isyl6FF6nqMTB9/ig5MrMRUXqZg",
            "ttl": 86400,
            "uris": ["turn:127.0.0.1:19302?transport=udp"]
          }
        */
        // 设置ICE服务器配置，使用固定的TURN服务器地址和凭证
        _iceServers = {
          'iceServers': [
            {
              // 'urls': _turnCredential['uris'][0],
              // 'username': _turnCredential['username'],
              // 'credential': _turnCredential['password']
              // 配置TURN服务器地址和传输协议
              'urls': 'turn:43.139.39.5:3478?transport=udp',
              // TURN服务器用户名
              'username': 'root',
              // TURN服务器密码
              'credential': 'root'
            },
          ]
        };
      } catch (e) {
        // 捕获异常但不做处理，保持程序继续执行
      }
    }

    // 设置WebSocket连接打开的回调函数
    _socket?.onOpen = () {
      // 打印连接成功信息
      print('onOpen');
      // 调用信令状态变化回调函数，通知连接已打开
      onSignalingStateChange?.call(SignalingState.ConnectionOpen);
      // 发送"new"消息到服务器，注册当前用户
      _send('new', {
        // 设备标签，标识设备信息
        'name': DeviceInfo.label,
        // 本地用户ID
        'id': _selfId,
        // 用户代理信息，标识客户端类型
        'user_agent': DeviceInfo.userAgent
      });
    };

    // 设置WebSocket接收到消息的回调函数
    _socket?.onMessage = (message) {
      // 打印接收到的消息内容
      print('Received data: ' + message);
      // 将接收到的JSON字符串解码并传递给onMessage方法处理
      onMessage(_decoder.convert(message));
    };

    // 设置WebSocket连接关闭的回调函数
    _socket?.onClose = (int? code, String? reason) {
      // 打印连接关闭信息，包括状态码和原因
      print('Closed by server [$code => $reason]!');
      // 调用信令状态变化回调函数，通知连接已关闭
      onSignalingStateChange?.call(SignalingState.ConnectionClosed);
    };

    // 等待WebSocket连接建立完成
    await _socket?.connect();
  }

  // 创建媒体流的异步方法，可选择摄像头或屏幕共享
  Future<MediaStream> createStream(String media, bool userScreen,
      {BuildContext? context}) async {
    // 媒体约束配置，设置音频和视频参数
    final Map<String, dynamic> mediaConstraints = {
      // 根据是否屏幕共享决定是否启用音频
      'audio': false,
      // 视频配置，设置理想的宽高
      'video': {
        'width': {'ideal': 1280},
        'height': {'ideal': 720},
      },
    };

    // 声明媒体流变量
    late MediaStream stream;
    // 如果是屏幕共享模式
    if (userScreen) {
      // 如果是桌面平台
      if (WebRTC.platformIsDesktop) {
        // 显示屏幕选择对话框，让用户选择要共享的屏幕或窗口
        final source = await showDialog<DesktopCapturerSource>(
          context: context!,
          builder: (context) => ScreenSelectDialog(),
        );
        // 获取用户选择的屏幕或窗口的媒体流
        stream = await navigator.mediaDevices.getDisplayMedia(<String, dynamic>{
          // 如果用户没有选择特定源，则使用默认屏幕共享
          'video': source == null
              ? true
              : {
            // 指定要捕获的设备ID
            'deviceId': {'exact': source.id},
            // 设置强制的帧率约束
            'mandatory': {'frameRate': 30.0}
          }
        });
      } else {
        // 非桌面平台，直接获取屏幕共享媒体流
        stream = await navigator.mediaDevices.getDisplayMedia(mediaConstraints);
      }
    } else {
      // 非屏幕共享模式，获取摄像头媒体流
      stream = await navigator.mediaDevices.getUserMedia(mediaConstraints);
    }

    // 调用本地流回调函数，通知本地流已创建
    onLocalStream?.call(stream);
    // 返回创建的媒体流
    return stream;
  }

  // 创建会话的异步方法，处理媒体流和RTCPeerConnection的初始化
  Future<Session> _createSession(
      Session? session, {
        required String peerId,
        required String sessionId,
        required String media,
        required bool screenSharing,
      }) async {
    // 如果传入的会话为空，则创建新会话
    var newSession = session ?? Session(sid: sessionId, pid: peerId);
    // 如果不是数据会话，则创建媒体流
    if (media != 'data')
      _localStream =
      await createStream(media, screenSharing, context: _context);
    // 打印ICE服务器配置信息
    print(_iceServers);
    // 创建RTCPeerConnection实例，配置ICE服务器和SDP语义
    RTCPeerConnection pc = await createPeerConnection({
      ..._iceServers,
      ...{'sdpSemantics': sdpSemantics}
    }, _config);

    // 如果不是数据会话，配置媒体流处理
    if (media != 'data') {
      // 根据SDP语义选择不同的配置方式
      switch (sdpSemantics) {
        case 'plan-b':
        // Plan-B模式下的流处理
          pc.onAddStream = (MediaStream stream) {
            // 调用远程流添加回调函数
            onAddRemoteStream?.call(newSession, stream);
            // 将远程流添加到远程流列表
            _remoteStreams.add(stream);
          };
          // 将本地流添加到RTCPeerConnection
          await pc.addStream(_localStream!);
          break;
        case 'unified-plan':
        // Unified-Plan模式下的流处理
          pc.onTrack = (event) {
            // 当检测到视频轨道时，调用远程流添加回调函数
            if (event.track.kind == 'video') {
              onAddRemoteStream?.call(newSession, event.streams[0]);
            }
          };
          // 将本地流的每个轨道添加到RTCPeerConnection
          _localStream!.getTracks().forEach((track) async {
            _senders.add(await pc.addTrack(track, _localStream!));
          });
          break;
      }

      // Unified-Plan: Simuclast模式配置示例（当前被注释掉）
      /*
      await pc.addTransceiver(
        track: _localStream.getAudioTracks()[0],
        init: RTCRtpTransceiverInit(
            direction: TransceiverDirection.SendOnly, streams: [_localStream]),
      );

      await pc.addTransceiver(
        track: _localStream.getVideoTracks()[0],
        init: RTCRtpTransceiverInit(
            direction: TransceiverDirection.SendOnly,
            streams: [
              _localStream
            ],
            sendEncodings: [
              RTCRtpEncoding(rid: 'f', active: true),
              RTCRtpEncoding(
                rid: 'h',
                active: true,
                scaleResolutionDownBy: 2.0,
                maxBitrate: 150000,
              ),
              RTCRtpEncoding(
                rid: 'q',
                active: true,
                scaleResolutionDownBy: 4.0,
                maxBitrate: 100000,
              ),
            ]),
      );*/
      /*
        var sender = pc.getSenders().find(s => s.track.kind == "video");
        var parameters = sender.getParameters();
        if(!parameters)
          parameters = {};
        parameters.encodings = [
          { rid: "h", active: true, maxBitrate: 900000 },
          { rid: "m", active: true, maxBitrate: 300000, scaleResolutionDownBy: 2 },
          { rid: "l", active: true, maxBitrate: 100000, scaleResolutionDownBy: 4 }
        ];
        sender.setParameters(parameters);
      */
    }

    // 设置ICE候选者回调函数
    pc.onIceCandidate = (candidate) async {
      // 当候选者为空时，表示候选者收集完成
      if (candidate == null) {
        print('onIceCandidate: complete!');
        return;
      }
      // 添加1秒延迟，让ICE候选者有足够时间尝试连接
      // 这是一个启发式值，需要根据实际环境测试调整
      await Future.delayed(
          const Duration(seconds: 1),
              () => _send('candidate', {
            // 目标用户ID
            'to': peerId,
            // 本地用户ID
            'from': _selfId,
            // ICE候选者信息
            'candidate': {
              'sdpMLineIndex': candidate.sdpMLineIndex,
              'sdpMid': candidate.sdpMid,
              'candidate': candidate.candidate,
            },
            // 会话ID
            'session_id': sessionId,
          }));
    };

    // 设置ICE连接状态回调函数（当前为空实现）
    pc.onIceConnectionState = (state) {};

    // 设置远程流移除回调函数
    pc.onRemoveStream = (stream) {
      // 调用远程流移除回调函数
      onRemoveRemoteStream?.call(newSession, stream);
      // 从远程流列表中移除指定流
      _remoteStreams.removeWhere((it) {
        return (it.id == stream.id);
      });
    };

    // 设置数据通道创建回调函数
    pc.onDataChannel = (channel) {
      // 添加数据通道到会话
      _addDataChannel(newSession, channel);
    };

    //将会话的RTCPeerConnection设置为创建的pc
    newSession.pc = pc;
    //返回创建的会话
    return newSession;
  }

  //添加数据通道到会话的方法
  void _addDataChannel(Session session, RTCDataChannel channel) {
    //设置数据通道状态变化回调函数（当前为空实现）
    channel.onDataChannelState = (e) {};
    //设置数据通道消息接收回调函数
    channel.onMessage = (RTCDataChannelMessage data) {
      //调用数据通道消息回调函数
      onDataChannelMessage?.call(session, channel, data);
    };
    //将会话的数据通道设置为当前通道
    session.dc = channel;
    //调用数据通道创建回调函数
    onDataChannel?.call(session, channel);
  }

  //创建数据通道的异步方法
  Future<void> _createDataChannel(Session session,
      {label = 'fileTransfer'}) async {
    //配置数据通道初始化参数，设置最大重传次数
    RTCDataChannelInit dataChannelDict = RTCDataChannelInit()
      ..maxRetransmits = 30;
    //创建数据通道
    RTCDataChannel channel =
    await session.pc!.createDataChannel(label, dataChannelDict);
    //添加数据通道到会话
    _addDataChannel(session, channel);
  }

  //创建会话邀请的异步方法
  Future<void> _createOffer(Session session, String media) async {
    try {
      //创建SDP offer，根据是否为数据会话使用不同的约束条件
      RTCSessionDescription s =
      await session.pc!.createOffer(media == 'data' ? _dcConstraints : {});
      //设置本地SDP描述，修复SDP参数
      await session.pc!.setLocalDescription(_fixSdp(s));
      //发送offer消息到对方
      _send('offer', {
        'to': session.pid,  //对方ID
        'from': _selfId,   //本地ID
        'description': {'sdp': s.sdp, 'type': s.type},  //SDP描述
        'session_id': session.sid,  //会话ID
        'media': media,  //媒体类型
      });
    } catch (e) {
      //打印异常信息
      print(e.toString());
    }
  }

  //修复SDP参数的方法，替换特定的配置参数
  RTCSessionDescription _fixSdp(RTCSessionDescription s) {
    var sdp = s.sdp;
    //替换H.264配置参数，将profile-level-id=640c1f替换为42e032
    s.sdp =
        sdp!.replaceAll('profile-level-id=640c1f', 'profile-level-id=42e032');
    return s;
  }

  //创建会话应答的异步方法
  Future<void> _createAnswer(Session session, String media) async {
    try {
      //创建SDP answer，根据是否为数据会话使用不同的约束条件
      RTCSessionDescription s =
      await session.pc!.createAnswer(media == 'data' ? _dcConstraints : {});
      //设置本地SDP描述，修复SDP参数
      await session.pc!.setLocalDescription(_fixSdp(s));
      //发送answer消息到对方
      _send('answer', {
        'to': session.pid,  //对方ID
        'from': _selfId,   //本地ID
        'description': {'sdp': s.sdp, 'type': s.type},  //SDP描述
        'session_id': session.sid,  //会话ID
      });
    } catch (e) {
      //打印异常信息
      print(e.toString());
    }
  }

  //发送消息到信令服务器的方法
  _send(event, data) {
    //构建消息对象
    var request = Map();
    request["type"] = event;  //消息类型
    request["data"] = data;   //消息数据
    //通过WebSocket发送JSON编码的消息
    _socket?.send(_encoder.convert(request));
  }

  //清理所有会话的异步方法
  Future<void> _cleanSessions() async {
    //如果本地流存在，停止所有轨道并释放资源
    if (_localStream != null) {
      _localStream!.getTracks().forEach((element) async {
        await element.stop();
      });
      await _localStream!.dispose();
      _localStream = null;
    }
    //遍历所有会话，关闭RTCPeerConnection和RTCDataChannel
    _sessions.forEach((key, sess) async {
      await sess.pc?.close();
      await sess.dc?.close();
    });
    //清空会话Map
    _sessions.clear();
  }

  //根据对方ID关闭会话的方法
  void _closeSessionByPeerId(String peerId) {
    var session;
    //从会话Map中移除与指定peerId相关的会话
    _sessions.removeWhere((String key, Session sess) {
      var ids = key.split('-');
      session = sess;
      //如果会话ID包含指定的peerId，则移除该会话
      return peerId == ids[0] || peerId == ids[1];
    });
    //如果找到了相关会话，则关闭该会话
    if (session != null) {
      _closeSession(session);
      //调用通话状态变化回调函数，通知会话已结束
      onCallStateChange?.call(session, CallState.CallStateBye);
    }
  }

  //关闭单个会话的异步方法
  Future<void> _closeSession(Session session) async {
    //停止本地流的所有轨道并释放资源
    _localStream?.getTracks().forEach((element) async {
      await element.stop();
    });
    await _localStream?.dispose();
    _localStream = null;

    //关闭会话的RTCPeerConnection和RTCDataChannel
    await session.pc?.close();
    await session.dc?.close();
    //清空发送者列表
    _senders.clear();
    //重置视频源为摄像头
    _videoSource = VideoSource.Camera;
  }
}
